package gui.edt.swingutilities;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.InvocationTargetException;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;

public class FreezeEDTSwingUtilitiesInvokeAndWait extends JFrame {

	private JLabel label;
	private JButton button;

	private boolean infinity = true;

	private final String MESS_LABEL = "Time elapsed : ";

	public FreezeEDTSwingUtilitiesInvokeAndWait() {
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		setLocationRelativeTo(null);
		setLayout(new BorderLayout());

		// Managing components
		label = new JLabel(MESS_LABEL + "0");
		button = new JButton("Start");
		add(label, BorderLayout.NORTH);
		add(button, BorderLayout.SOUTH);

		// Managing events
		manageButton();

		pack();
	}

	private void manageButton() {
		// Definition of local class :)
		class ButtonListener implements ActionListener {

			private boolean firstClic = true;

			@Override
			public void actionPerformed(ActionEvent e) {
				if (firstClic) {
					firstClic = false;
					button.setBackground(Color.white);
					timer();
				} else {
					button.setText("Stop");
					button.setBackground(Color.RED);
					Thread t = new Thread(new Runnable() {
						
						@Override
						public void run() {
							/*
							 * BE AWARE that here, we are in the EDT !!
							 * So if we want to call .invokeAndWait method, we need to run another thread...
							 * since a call to .invokeAndWait method blocks the CURRENT thread (it is exactly
							 * what we do not want as regards EDT :)
							 */
							proposeDialog();							
						}
					});
					t.setName("Thread Dialog");
					t.start();
				}
			}
		}

		button.addActionListener(new ButtonListener());
	}

	private void proposeDialog() {
		System.out.println("Something else ?");
		
		/*
		 * This is a clever way to be able to get the int value.
		 * Indeed, in an anonymous class (or local class or inner class),
		 * we can only reach static members, instance members of final local variables...
		 * so a final int[] (which size is one) is a good way to reach an int from an inner class.
		 */
		final int[] ans = new int[1];
		
		final String message = "Do you want to stop the timer ?";
		Thread currT = Thread.currentThread();
		String nameCurrT = currT.getName();
		System.out.println(nameCurrT);

		try {
				/*
				 * BE CAREFUL !! This method MUST NOT be called from the EDT !! 
				 * Otherwise, the GUI will freeze !!
				 * The idea is to call the SwingUtilities.invokeAndWait method from another thread !!
				 * And this this will be blocked until the .invokeAndWait completion. 
				 */
				SwingUtilities.invokeAndWait(new Runnable() {

					@Override
					public void run() {
						Thread currT = Thread.currentThread();
						String nameCurrT = currT.getName();
						System.out.println(nameCurrT);
						
						
						ans[0] = JOptionPane.showConfirmDialog(
								FreezeEDTSwingUtilitiesInvokeAndWait.this,
								message, message, JOptionPane.YES_NO_OPTION);
						if (ans[0] == JOptionPane.YES_OPTION) {
							infinity = false;
							button.setText("Start");
							button.setBackground(Color.WHITE);
							return;
						}
					}
				});
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	private void timer() {
		final int CST_TIME = 10 * 5;
		final int[] timeElapsed = new int[1];
		class Worker implements Runnable {

			@Override
			public void run() {
				while (infinity) {
					try {
						Thread.sleep(100);
						if (timeElapsed[0] == CST_TIME) {
							proposeDialog();
						}
						timeElapsed[0]++;
						SwingUtilities.invokeLater(new Runnable() {

							@Override
							public void run() {
								label.setText(MESS_LABEL + timeElapsed[0]);
								pack();
							}
						});
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		};
		new Thread(new Worker()).start();
	}

	public static void main(String[] args) {
		new FreezeEDTSwingUtilitiesInvokeAndWait().setVisible(true);
	}
}
